﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TvdP
{
    /// <summary>
    /// Interface for a composed adapter system.
    /// </summary>
    /// <remarks>
    /// It uses a registry of many specific adapters to support adaptations from objects of some type to another type. It finds adaptation pathways in the whole registry of specific adapters.
    /// 
    /// For each adaptation 'solution' it remembers which object it wase originaly adapted from ('base'). This base can never be garbage collected if a strong reference to any of it's adaptations is held. 
    /// Conversely a 'solution' can be garbage collected when a strong reference to it's 'base' is held. Of a given 'base' there will be only one 'solution' of any given type.
    /// 
    /// When adapting an object that itself is a 'solution' the adaptation will start from its 'base' object and not the object itself. 
    /// </remarks>
    public interface IAdapters
    {
        /// <summary>
        /// Attempts to adapt <paramref name="adaptee"/> to type <paramref name="toType"/>
        /// </summary>
        /// <param name="adaptee"></param>
        /// <param name="toType"></param>
        /// <returns></returns>
        /// <remarks>
        /// This method always goes 'through the base'. <paramref name="adaptee"/> itself can be be an adaption of another object. 
        /// First the most original object (base) is found and that is converted to the target type. 
        /// </remarks>
        object Cast(object adaptee, Type toType);

        /// <summary>
        /// Gets the base for a particular solution
        /// </summary>
        /// <param name="solution"></param>
        /// <returns></returns>
        object GetBase(object solution);

        /// <summary>
        /// Determines if an adaptation from type <paramref name="fromType"/> to type <paramref name="toType"/> is possible without regarding the base.
        /// </summary>
        /// <param name="fromType"></param>
        /// <param name="toType"></param>
        /// <returns>True if the type can be adapted; false otherwise</returns>
        /// <remarks>
        /// The Cast method goes through the 'base' of an object, so finding out if an object instance can be converted to a specific type should be querried like:
        /// store.CanCast(store.GetBase(obj).GetType(), toType)
        /// </remarks>
        bool CanCast(Type fromType, Type toType);
    }

    /// <summary>
    /// Version of IAdapter that adapts to a specific type.
    /// </summary>
    /// <typeparam name="ToType">The type to adapt to</typeparam>
    public interface IAdapters<ToType>
        where ToType : class
    {
        /// <summary>
        /// Attempts to adapt <paramref name="adaptee"/> to type <typeparamref name="ToType"/>
        /// </summary>
        /// <param name="adaptee"></param>
        /// <returns></returns>
        /// <remarks>
        /// This method always goes 'through the base'. <paramref name="adaptee"/> itself can be be an adaption of another object. 
        /// First the most original object (base) is found and that is converted to the target type. 
        /// </remarks>
        ToType Cast(object adaptee);

        /// <summary>
        /// Gets the base for a particular solution
        /// </summary>
        /// <param name="solution"></param>
        /// <returns></returns>
        object GetBase(ToType solution);

        /// <summary>
        /// Determines if a cast from type <paramref name="fromType"/> to type <typeparamref name="ToType"/> is possible without regarding the base.
        /// </summary>
        /// <param name="fromType"></param>
        /// <returns>True if the type can be adapted; false otherwise</returns>
        /// <remarks>
        /// The Cast method goes through the 'base' of an object, so finding out if a specific object instance can be cast to a specific type should be querried like:
        /// adapters.CanCast(store.GetBase(obj).GetType(), toType)
        /// </remarks>
        bool CanCast(Type fromType);
    }
}
